Utils = {
    MapMarkIDs = {
        [1] = true,
    },
}

do
    function Utils.getMarkID()
        local result = nil

        for i,value in ipairs(Utils.MapMarkIDs) do
            if value then
                result = i
                Utils.MapMarkIDs[i] = false
                break
            end
        end

        if not result then
            result = #Utils.MapMarkIDs + 1
            Utils.MapMarkIDs[result] = false
        end

        return result
    end

    function Utils.Debug(text,displayTime,clearview)
        if not text then text = 'nil or false' end
        
        if type(text) ~= 'string' then
            
            if type(text) ~= 'table' then
                text = tostring(text)
            end

            if type(text) == 'table' then
                if mist then
                    text = mist.utils.tableShow(text)
                else
                    text = 'no mist, please use mist to debug a table.'
                end
            end
        end

        displayTime = displayTime or 10
        clearview = clearview or false

        trigger.action.outText(text,displayTime,clearview)
    end
    
    function Utils.getTableSize(table)
        if not table then return end

        local tableSize = 0
        for _,_ in pairs(table) do
            tableSize = tableSize + 1
        end

        return tableSize
    end

    --- Creates a deep copy of a object.
	-- Usually this object is a table.
	-- See also: from http://lua-users.org/wiki/CopyTable
	-- @param object object to copy
	-- @return copy of object
	function Utils.deepCopy(object)
		local lookup_table = {}
		local function _copy(object)
			if type(object) ~= "table" then
				return object
			elseif lookup_table[object] then
				return lookup_table[object]
			end
			local new_table = {}
			lookup_table[object] = new_table
			for index, value in pairs(object) do
				new_table[_copy(index)] = _copy(value)
			end
			return setmetatable(new_table, getmetatable(object))
		end
		return _copy(object)
	end


    --- Vector addition.
	-- @tparam Vec3 vec1 first vector
	-- @tparam Vec3 vec2 second vector
	-- @treturn Vec3 new vector, sum of vec1 and vec2.
    function Utils.vecAdd(vec1, vec2)
		return {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z}
	end

	--- Vector substraction.
	-- @tparam Vec3 vec1 first vector
	-- @tparam Vec3 vec2 second vector
	-- @treturn Vec3 new vector, vec2 substracted from vec1.
	function Utils.vecSub(vec1, vec2)
		return {x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = vec1.z - vec2.z}
	end

    function Utils.vecTranslate(vec3,degree,distance)
        local point = {x = vec3.x,y = vec3.z or vec3.y,}
        
        if distance == 0 then return point end
    
        local radian = math.rad(degree)
    
        point.x = point.x + distance * math.cos(radian)
        point.y = point.y + distance * math.sin(radian)
        return point
    end

	function Utils.makeVec3(vec,AGL)
		local result = Utils.deepCopy(vec)
        if not vec.z then
            result.z = vec.y
            result.y = 0
            if AGL then 
                result.y = land.getHeight(vec)
            end
        end

        return result
	end

    --- Vector magnitude
	-- @tparam Vec3 vec vector
	-- @treturn number magnitude of vector vec
    function Utils.mag(vec)
		return (vec.x^2 + vec.y^2 + vec.z^2)^0.5
	end

    --- Returns distance in meters between two points.
	-- @tparam Vec2|Vec3 point1 first point
	-- @tparam Vec2|Vec3 point2 second point
	-- @treturn number distance between given points.
	function Utils.get2DDist(point1, point2)
        if not point1 then
            log:warn("Utils.get2DDist 1st input value is nil") 
        end
        if not point2 then
            log:warn("Utils.get2DDist 2nd input value is nil") 
        end
		point1 = Utils.makeVec3(point1)
		point2 = Utils.makeVec3(point2)
		return Utils.mag({x = point1.x - point2.x, y = 0, z = point1.z - point2.z})
	end

	--- Returns distance in meters between two points in 3D space.
	-- @tparam Vec3 point1 first point
	-- @tparam Vec3 point2 second point
	-- @treturn number distancen between given points in 3D space.
	function Utils.get3DDist(point1, point2)
        if not point1 then
            log:warn("Utils.get3DDist  1st input value is nil") 
        end
        if not point2 then
            log:warn("Utils.get3DDist  2nd input value is nil") 
        end
		return Utils.mag({x = point1.x - point2.x, y = point1.y - point2.y, z = point1.z - point2.z})
	end

	-- @return radians
	function Utils.getObjHeading(object)
        if not object then return false end

        local objectPos = object:getPosition()
        local objectHeading = math.atan2(objectPos.x.z, objectPos.x.x)

        return objectHeading
    end

    function Utils.getNorthCorrection(gPoint)	--gets the correction needed for true north
		local point = Utils.deepCopy(gPoint)
		if not point.z then --Vec2; convert to Vec3
			point.z = point.y
			point.y = 0
		end
		local lat, lon = coord.LOtoLL(point)
		local north_posit = coord.LLtoLO(lat + 1, lon)
		return math.atan2(north_posit.z - point.z, north_posit.x - point.x)
	end

	--- Returns heading-error corrected direction.
	-- True-north corrected direction from point along vector vec.
	-- @tparam Vec3 vec
	-- @tparam Vec2 point
	-- @return heading-error corrected direction from point.
	function Utils.getDir(vec, point)
		local dir = math.atan2(vec.z, vec.x)
		if point then
			dir = dir + Utils.getNorthCorrection(point)
		end
		if dir < 0 then
			dir = dir + 2 * math.pi	-- put dir in range of 0 to 2*pi
		end
		return dir
	end

    function Utils.getDirection(vec3_1,vec3_2)
        local p1 = Utils.makeVec3(vec3_1,true)
        local p2 = Utils.makeVec3(vec3_2,true)
        local dir = Utils.getDir(Utils.vecSub(p1, p2))

        return dir
    end

    function Utils.getAvgPoint(points)
        local avgX, avgY, avgZ, totNum = 0, 0, 0, 0
        for i = 1, #points do
            --log:warn(points[i])
            local nPoint = Utils.makeVec3(points[i])
            if nPoint.z then
                avgX = avgX + nPoint.x
                avgY = avgY + nPoint.y
                avgZ = avgZ + nPoint.z
                totNum = totNum + 1
            end
        end
        if totNum ~= 0 then
            return {x = avgX/totNum, y = avgY/totNum, z = avgZ/totNum}
        end
    end

    -- need to return a Vec3 or Vec2?
    function Utils.getRandPointInCircle(p, r, innerRadius, maxA, minA)
        local point = Utils.makeVec3(p)
        local theta = 2*math.pi*math.random()
        local radius = r or 1000
        local minR = innerRadius or 0
        if maxA and not minA then
            theta = math.rad(math.random(0, maxA - math.random()))
        elseif maxA and minA then
            if minA < maxA then
                theta = math.rad(math.random(minA, maxA) - math.random())
            else
                theta = math.rad(math.random(maxA, minA) - math.random())
            end
        end
        local rad = math.random() + math.random()
        if rad > 1 then
            rad = 2 - rad
        end

        local radMult
        if minR and minR <= radius then
            --radMult = (radius - innerRadius)*rad + innerRadius
            radMult = radius * math.sqrt((minR^2 + (radius^2 - minR^2) * math.random()) / radius^2)
        else
            radMult = radius*rad
        end

        local rndCoord
        if radius > 0 then
            rndCoord = {x = math.cos(theta)*radMult + point.x, y = math.sin(theta)*radMult + point.z}
        else
            rndCoord = {x = point.x, y = point.z}
        end
        return rndCoord
    end

    function Utils.getRandomPointInPoly(zone)
		--env.info('Zone Size: '.. #zone)
        local avg = Utils.getAvgPoint(zone)
        --log:warn(avg)
		local radius = 0
		local minR = math.huge
		local newCoord = {}
		for i = 1, #zone do
			if Utils.get2DDist(avg, zone[i]) > radius then
				radius = Utils.get2DDist(avg, zone[i])
			end
			if Utils.get2DDist(avg, zone[i]) < minR then
				minR = Utils.get2DDist(avg, zone[i])
			end
		end
        --log:warn('minR: $1', minR)
        --log:warn('Radius: $1', radius)
		local lSpawnPos = {}
		for j = 1, 100 do
			newCoord = Utils.getRandPointInCircle(avg, radius)
			if Utils.pointInPolygon(newCoord, zone) then
				break
			end
			if j == 100 then
				newCoord = Utils.getRandPointInCircle(avg, radius)
				log:warn("Failed to find point in poly; Giving random point from center of the poly")
			end
		end
		return newCoord
	end

    --- Simple rounding function.
	-- From http://lua-users.org/wiki/SimpleRound
	-- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place
	-- @tparam number num number to round
	-- @param idp
	function Utils.round(num, idp)
		local mult = 10^(idp or 0)
		return math.floor(num * mult + 0.5) / mult
	end

	--[[acc:
	in DM: decimal point of minutes.
	In DMS: decimal point of seconds.
	position after the decimal of the least significant digit:
	So:
	42.32 - acc of 2.
	]]
	function Utils.tostringLL(lat, lon, acc, DMS)

		local latHemi, lonHemi
		if lat > 0 then
			latHemi = 'N'
		else
			latHemi = 'S'
		end

		if lon > 0 then
			lonHemi = 'E'
		else
			lonHemi = 'W'
		end

		lat = math.abs(lat)
		lon = math.abs(lon)

		local latDeg = math.floor(lat)
		local latMin = (lat - latDeg)*60

		local lonDeg = math.floor(lon)
		local lonMin = (lon - lonDeg)*60

		if DMS then	-- degrees, minutes, and seconds.
			local oldLatMin = latMin
			latMin = math.floor(latMin)
			local latSec = Utils.round((oldLatMin - latMin)*60, acc)

			local oldLonMin = lonMin
			lonMin = math.floor(lonMin)
			local lonSec = Utils.round((oldLonMin - lonMin)*60, acc)

			if latSec == 60 then
				latSec = 0
				latMin = latMin + 1
			end

			if lonSec == 60 then
				lonSec = 0
				lonMin = lonMin + 1
			end

			local secFrmtStr -- create the formatting string for the seconds place
			if acc <= 0 then	-- no decimal place.
				secFrmtStr = '%02d'
			else
				local width = 3 + acc	-- 01.310 - that's a width of 6, for example.
				secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
			end

			return string.format('%02d', latDeg) .. ' ' .. string.format('%02d', latMin) .. '\' ' .. string.format(secFrmtStr, latSec) .. '"' .. latHemi .. '	 '
			.. string.format('%02d', lonDeg) .. ' ' .. string.format('%02d', lonMin) .. '\' ' .. string.format(secFrmtStr, lonSec) .. '"' .. lonHemi

		else	-- degrees, decimal minutes.
			latMin = Utils.round(latMin, acc)
			lonMin = Utils.round(lonMin, acc)

			if latMin == 60 then
				latMin = 0
				latDeg = latDeg + 1
			end

			if lonMin == 60 then
				lonMin = 0
				lonDeg = lonDeg + 1
			end

			local minFrmtStr -- create the formatting string for the minutes place
			if acc <= 0 then	-- no decimal place.
				minFrmtStr = '%02d'
			else
				local width = 3 + acc	-- 01.310 - that's a width of 6, for example.
				minFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
			end

			return string.format('%02d', latDeg) .. ' ' .. string.format(minFrmtStr, latMin) .. '\'' .. latHemi .. '	 '
			.. string.format('%02d', lonDeg) .. ' ' .. string.format(minFrmtStr, lonMin) .. '\'' .. lonHemi

		end
	end

    --- Converts a TACAN Channel/Mode couple into a frequency in Hz.
    -- @param #number TACANChannel The TACAN channel, i.e. the 10 in "10X".
    -- @param #string TACANMode The TACAN mode, i.e. the "X" in "10X".
    -- @return #number Frequency in Hz or #nil if parameters are invalid.
    function Utils.TACANToFrequency(TACANChannel, TACANMode)

        if type(TACANChannel) ~= "number" then
        return nil -- error in arguments
        end
        if TACANMode ~= "X" and TACANMode ~= "Y" then
        return nil -- error in arguments
        end
    
        -- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137.
        -- I have no idea what it does but it seems to work
        local A = 1151 -- 'X', channel >= 64
        local B = 64   -- channel >= 64
    
        if TACANChannel < 64 then
        B = 1
        end
    
        if TACANMode == 'Y' then
        A = 1025
        if TACANChannel < 64 then
            A = 1088
        end
        else -- 'X'
        if TACANChannel < 64 then
            A = 962
        end
        end
  
        return (A + TACANChannel - B) * 1000000
    end


    function Utils.getWindBearingAndVel(point)
        local tPoint = Utils.makeVec3(point)
        local gLevel = land.getHeight({x = tPoint.x, y = tPoint.z})
        if tPoint.y <= gLevel then
            tPoint.y = gLevel + 10
        end
        local t = atmosphere.getWind(tPoint)
        local bearing = math.atan2(t.z, t.x)
        local vel = math.sqrt(t.x^2 + t.z^2)

        return bearing, vel
    end


    --- Returns an array containing a group's units positions.
	--	e.g.
	--		{
	--			[1] = {x = 299435.224, y = -1146632.6773},
	--			[2] = {x = 663324.6563, y = 322424.1112}
	--		}
	--	@tparam number|string groupIdent group id or name
	--	@treturn table array containing positions of each group member
	function Utils.getWayPoints(groupIdent)
		-- search by groupId and allow groupId and groupName as inputs
		local gpId = groupIdent
		if type(groupIdent) == 'string' and not tonumber(groupIdent) then
			local group = Group.getByName(groupIdent)
            if not group then return end

            gpId = group:getID()
		end

		for coa_name, coa_data in pairs(env.mission.coalition) do
			if  type(coa_data) == 'table' then
				if coa_data.country then --there is a country table
					for cntry_id, cntry_data in pairs(coa_data.country) do
						for obj_cat_name, obj_cat_data in pairs(cntry_data) do
							if obj_cat_name == "helicopter" or obj_cat_name == "ship" or obj_cat_name == "plane" or obj_cat_name == "vehicle" then	-- only these types have points
								if ((type(obj_cat_data) == 'table') and obj_cat_data.group and (type(obj_cat_data.group) == 'table') and (#obj_cat_data.group > 0)) then	--there's a group!
									for group_num, group_data in pairs(obj_cat_data.group) do
										if group_data and group_data.groupId == gpId then -- this is the group we are looking for
											if group_data.route and group_data.route.points and #group_data.route.points > 0 then
												local points = {}
												for point_num, point in pairs(group_data.route.points) do
													if not point.point then
														points[point_num] = { x = point.x, y = point.y }
													else
														points[point_num] = point.point	--it's possible that the ME could move to the point = Vec2 notation.
													end
												end
												return points
											end
											return
										end	--if group_data and group_data.name and group_data.name == 'groupname'
									end --for group_num, group_data in pairs(obj_cat_data.group) do
								end --if ((type(obj_cat_data) == 'table') and obj_cat_data.group and (type(obj_cat_data.group) == 'table') and (#obj_cat_data.group > 0)) then
							end --if obj_cat_name == "helicopter" or obj_cat_name == "ship" or obj_cat_name == "plane" or obj_cat_name == "vehicle" or obj_cat_name == "static" then
						end --for obj_cat_name, obj_cat_data in pairs(cntry_data) do
					end --for cntry_id, cntry_data in pairs(coa_data.country) do
				end --if coa_data.country then --there is a country table
			end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then
		end --for coa_name, coa_data in pairs(mission.coalition) do
	end

    function Utils.pointInPolygon(point, poly, maxalt)
        point = Utils.makeVec3(point)
        local px = point.x
        local pz = point.z
        local cn = 0
        local newpoly = Utils.deepCopy(poly)
    
        if not maxalt or (point.y <= maxalt) then
            local polysize = #newpoly
            newpoly[#newpoly + 1] = newpoly[1]
    
            newpoly[1] = Utils.makeVec3(newpoly[1])
    
            for k = 1, polysize do
                newpoly[k+1] = Utils.makeVec3(newpoly[k+1])
                if ((newpoly[k].z <= pz) and (newpoly[k+1].z > pz)) or ((newpoly[k].z > pz) and (newpoly[k+1].z <= pz)) then
                    local vt = (pz - newpoly[k].z) / (newpoly[k+1].z - newpoly[k].z)
                    if (px < newpoly[k].x + vt*(newpoly[k+1].x - newpoly[k].x)) then
                        cn = cn + 1
                    end
                end
            end
    
            return cn%2 == 1
        else
            return false
        end
    end

    function Utils.doString(s)
        local f, err = loadstring(s)
        if f then
            return true, f()
        else
            return false, err
        end
    end

    function Utils.drawPolygon(Coalition,MarkID,vecs,Color,FillColor,LineType,ReadOnly,Text)
        local s=string.format("trigger.action.markupToAll(7, %d, %d,", Coalition, MarkID)
        for _,vec in pairs(vecs) do   
            vec = Utils.makeVec3(vec)
            --s=s..string.format("%s,", UTILS._OneLineSerialize(vec))
            s=s..string.format("{x=%.1f, y=%.1f, z=%.1f},", vec.x, vec.y, vec.z)
        end
        s=s..string.format("{%.3f, %.3f, %.3f, %.3f},", Color[1], Color[2], Color[3], Color[4])
        s=s..string.format("{%.3f, %.3f, %.3f, %.3f},", FillColor[1], FillColor[2], FillColor[3], FillColor[4])
        s=s..string.format("%d,", LineType or 1)
        s=s..string.format("%s", tostring(ReadOnly))
        if Text and type(Text)=="string" and string.len(Text)>0 then
            s=s..string.format(", \"%s\"", tostring(Text))
        end
        s=s..")"

        -- Execute string command
        local success=Utils.doString(s)
                
        if not success then
            self:E("ERROR: Could not draw polygon")
            env.info(s)
        end
    end















end